#pragma once

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/wait.h>
#include <pthread.h>
#include <string.h>

#include "log.hpp"
#include "Inetaddr.hpp"
#include "threadpool.hpp"

using namespace log_namespace;
using namespace threadModel;

enum
{
    SOCKET_ERROR = 1
    ,BIND_ERROR
    ,LISTEN_ERROR
};

const static int gport = 8888;
const static int gsock = -1;
const static int gbacklog = 8;

using task_t = std::function<void()>;

class tcpServer
{    
public:
    tcpServer(uint16_t port = gport)
        :_port(gport)
        ,_listensocketfd(gsock)
        ,_isrunning(false)
    {
        
    }

    void initServer()
    {
        _listensocketfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensocketfd < 0)
        {
            exit(SOCKET_ERROR);
            LOG(FATAL, "socket create error\n");
        }
        LOG(INFO, "socket create success, socketfd: %d\n", _listensocketfd);

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        // 绑定本主机的套接字和 对方主机的地址和端口号
        if(::bind(_listensocketfd, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            LOG(FATAL, "bind error!\n");
            exit(BIND_ERROR);
        }
        LOG(INFO, "bind success\n");

        // tcp是面向连接的，tcp需要未来不断地获取链接
        if (::listen(_listensocketfd, gbacklog) < 0)
        {
            LOG(FATAL, "bind error\n");
            exit(LISTEN_ERROR);
        }
        LOG(INFO, "listen success\n");

    }

    class threadData
    {
    public:
        threadData(inetAddr addr, tcpServer* tmp, int socketfd)
            :_addr(addr)
            ,_self(tmp)
            ,_socketfd(socketfd)
        {}
        inetAddr _addr;
        tcpServer* _self;
        int _socketfd;
    };

    // version 2: 多线程版本
    static void *execute(void* args)
    {
        threadData* auxitmp = static_cast<threadData*>(args);
        auxitmp->_self->service(auxitmp->_socketfd, auxitmp->_addr);
        delete auxitmp;
        return nullptr;
    }

    void loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int socketfd = ::accept(_listensocketfd, (struct sockaddr*)&client, &len);
            if (socketfd < 0)
            {
                LOG(WARNING, "accept error\n");
                continue;
            }
            inetAddr addr(client);
            LOG(INFO, "get a new link, client info: %s\n, socketfd is: %d\n", addr.addrStr().c_str(), socketfd);
            
            pthread_t tid;
            // using func_t = std::function<void(const std::string& )>;
            threadData* auxitmp = new threadData(addr, this, socketfd);
            pthread_create(&tid, 0, execute, (void*)auxitmp);
        }
        _isrunning = false;
    }

    void service(int socketfd, inetAddr addr)
    {
        // 如果按照如下设计 服务，那么一旦客户端连上了服务器
        // 只要客户端没有退出，服务器端就会一直为这个客户端提供服务
        // 这种服务称为长服务
        while (true)
        {
            char inbuffer[1024];
            ssize_t n = ::recv(socketfd, inbuffer, sizeof(inbuffer) - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                LOG(INFO, "get message from client %s, message: %s\n", addr.addrStr().c_str(), inbuffer);
                std::string echo_string = "[server echo]# ";
                echo_string += inbuffer;
                ::send(socketfd, echo_string.c_str(), echo_string.size(), 0);
            }
            else if(n == 0)
            {
                LOG(INFO, "client %s quit\n", addr.addrStr().c_str());
                break;
            }
            else
            {
                LOG(ERROR, "read error: %s\n", addr.addrStr().c_str());
                break;
            }
        }
        ::close(socketfd);
    }

    ~tcpServer()
    {

    }
private:
    uint16_t _port;
    int _listensocketfd;
    bool _isrunning;
};


